﻿/*
COPYRIGHT (C) 2011-2013 Raynald Messié

THIS FILE IS PART OF SCRUMPILOT.

SCRUMPILOT IS FREE SOFTWARE: YOU CAN REDISTRIBUTE IT AND/OR MODIFY IT UNDER THE TERMS OF 
THE GNU LESSER GENERAL PUBLIC LICENSE VERSION v2.1 AS PUBLISHED BY THE FREE SOFTWARE FOUNDATION.

SCRUMPILOT IS DISTRIBUTED IN THE HOPE THAT IT WILL BE USEFUL, BUT WITHOUT ANY WARRANTY; WITHOUT
EVEN THE IMPLIED WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. SEE THE GNU LESSER
GENERAL PUBLIC LICENSE FOR MORE DETAILS.

YOU SHOULD HAVE RECEIVED A COPY OF THE GNU LESSER GENERAL PUBLIC LICENSE ALONG WITH SCRUMPILOT. 
IF NOT, SEE <HTTP://WWW.GNU.ORG/LICENSES/>.
*/

using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Threading.Tasks;
using System.Web;
using Microsoft.TeamFoundation.Server;
using ScrumPilot.Domain.Models;
using ScrumPilot.Helpers;
using ScrumPilot.TFSHelpers;
using WorkItem = Microsoft.TeamFoundation.WorkItemTracking.Client.WorkItem;

namespace ScrumPilot.Domain.BPL
{
    public class UserServices : BusinessProcessLayer
    {
        public UserServices() { }

        public UserServices(TFSServices tfs) : base(tfs) { }

        public UserServices(TFSServices tfs, String tfsRootUrl) : base(tfs, tfsRootUrl) { }

#pragma warning disable 1998
        public async Task<UserDetailledWorkItems> GetDetailledWorkItemsAsync(Int32 id)
#pragma warning restore 1998
        {
            var lst = (new WorkItemServices(TFS)).GetWorkItemsExtUser(id);
            var oRet = new UserDetailledWorkItems
                           {
                               UserStories =
                                   this.CopyAndMapFromTFS(
                                       lst.Where(
                                           workItem =>
                                           workItem.GetWorkItemType() == WorkItemType.UserStory)),
                               Tasks =
                                   this.CopyAndMapFromTFS(
                                       lst.Where(
                                           workItem =>
                                           workItem.GetWorkItemType() == WorkItemType.Task)),
                               Bugs =
                                   this.CopyAndMapFromTFS(
                                       lst.Where(
                                           workItem =>
                                           workItem.GetWorkItemType() == WorkItemType.Defect)),
                               CheckOuts = this.GetCheckOutUserList(id)
                           };
            this.SetInfoDataonUser(id, lst, ref oRet);
            return oRet;
        }

        // ReSharper disable CSharpWarnings::CS1998
        public async Task<int[]> HistoryBugs(Int32 id)
        // ReSharper restore CSharpWarnings::CS1998
        {
            using (var unitOfWork = new UnitOfWork())
            {
                user usr = unitOfWork.UserRepositery.GetById(id);
                if (usr != null)
                {
                    var dateList = DateTimeHelpers.GetDateWorkDays(DateTime.UtcNow, Range);
                    var qry = new TFSQuery(TFS.TfsWorkItemStore);
                    var bugsList =
                        qry.GetCompletedWorkItemList(dateList[0], usr.name).OrderByDescending(p => p.GetResolvedDate()).ToArray();
                    var iRet = dateList.Select(
                        date =>
                        bugsList.Count(p => p.GetResolvedDate() != null && p.GetResolvedDate().Value.Date == date.Date))
                                       .ToArray();

                    return iRet;
                }
                return null;
            }
        }

        // ReSharper disable CSharpWarnings::CS1998
        public async Task<Tuple<List<int>, List<decimal>, List<int>>> HistoryTasks(Int32 id)
        // ReSharper restore CSharpWarnings::CS1998
        {
            using (var unitOfWork = new UnitOfWork())
            {
                user usr = unitOfWork.UserRepositery.GetById(id);
                if (usr != null)
                {
                    var iRet = new Tuple<List<int>, List<decimal>, List<int>>(new List<int>(), new List<decimal>(), new List<int>());
                    var dateList = DateTimeHelpers.GetDateWorkDays(DateTime.UtcNow, Range);
                    var qry = new TFSQuery(TFS.TfsWorkItemStore);
                    var tasksList =
                        qry.GetCompletedWorkItemList(dateList[0], usr.name, 1).OrderByDescending(p => p.GetClosedDate()).ToArray();
                    foreach (var myList in dateList.Select(dateTime => tasksList.Where(p => p.GetClosedDate() != null && p.GetClosedDate().Value.Date == dateTime)).ToArray())
                    {
                        iRet.Item1.Add(myList.Count());
                        iRet.Item2.Add(myList.Sum(s => s.GetCompletedWork()));
                        iRet.Item3.Add(myList.Sum(s => s.GetScrumPilotScore()));
                    }

                    return iRet;
                }
                return null;
            }
        }

        // ReSharper disable CSharpWarnings::CS1998
        public async Task<TFSTimeLineObject[]> HistoryUser(Int32 id)
        // ReSharper restore CSharpWarnings::CS1998
        {
            using (var unitOfWork = new UnitOfWork())
            {
                user usr = unitOfWork.UserRepositery.GetById(id);
                if (usr != null)
                {
                    var dateList = DateTimeHelpers.GetDateWorkDays(DateTime.UtcNow, Range);
                    var qry = new TFSQuery(TFS.TfsWorkItemStore);
                    // first : querying WorkItems DB
                    var workItemList = ArrangUserActions(qry.GetWorkItemListForUser(usr.name, dateList[0]), dateList[0], usr.name);

                    // second : querying changeset
                    var changeSets = TFS.GetHistoryChangeSet(usr.name, dateList[0].Date);

                    workItemList.AddRange(changeSets);

                    workItemList = new List<TFSTimeLineObject>(workItemList.OrderByDescending(p => p.Date));
                    foreach (var item in workItemList.Where(item => item.Descriptions.Length > 256))
                    {
                        item.Descriptions = item.Descriptions.Substring(0, 256) +
                                            "... (more details in TFS)";
                    }
                    return workItemList.ToArray();
                }
                return null;
            }
        }

        private List<TFSTimeLineObject> ArrangUserActions(List<WorkItem> workItemsList, DateTime limitDate,
                                                             string userName)
        {
            var wallContents = new List<TFSTimeLineObject>();

            foreach (var workItem in workItemsList)
            {
                if (workItem.GetState() != WorkItemState.Done && workItem.GetAssignedTo() == userName)
                {
                    wallContents.Add(ContentWithDate(workItem, DateTime.UtcNow, "Work on"));
                }
                if (workItem.GetWorkItemType() == WorkItemType.Defect)
                {
                    if (workItem.GetResolvedDate() != null && workItem.GetResolvedDate() >= limitDate &&
                        workItem.GetResolvedBy() == userName)
                    {
                        wallContents.Add(ContentWithDate(workItem, workItem.GetResolvedDate().Value, "Resolved"));
                    }
                }
                if (workItem.CreatedDate >= limitDate && workItem.CreatedBy == userName)
                {
                    wallContents.Add(ContentWithDate(workItem, workItem.CreatedDate, "Created"));
                }
                if (workItem.ChangedDate >= limitDate && workItem.ChangedBy == userName)
                {
                    wallContents.Add(ContentWithDate(workItem, workItem.ChangedDate, "Updated"));
                }
                if (workItem.GetClosedDate() != null && workItem.GetClosedDate() >= limitDate && workItem.GetClosedBy() == userName)
                {
                    wallContents.Add(ContentWithDate(workItem, workItem.GetClosedDate().Value, "Closed"));
                }
                if (workItem.GetStateChangeDate() != null && workItem.GetStateChangeDate() >= limitDate && workItem.GetStateChangeBy() == userName)
                {
                    wallContents.Add(ContentWithDate(workItem, workItem.GetStateChangeDate().Value, "Status Changed"));
                }
            }
            return wallContents;
        }

        private TFSTimeLineObject ContentWithDate(WorkItem item, DateTime date, String state)
        {
            var myContent = item.CastToTimeLineObject();
            myContent.Date = date;
            myContent.State = state;
            return myContent;
        }


        private IList<WorkItemSimpleForUser> GetCheckOutUserList(Int32 id)
        {
            var items = (new SourcesServices(TFS)).GetCheckOutUser(id);
            if (items == null)
                return null;
            var aRet = new List<WorkItemSimpleForUser>(items.Count());
            var templateUrl = this.TFSRootUrl + "{0}/_VersionControl#path={1}&_a=contents";

            aRet.AddRange(items.Select(workItem => new WorkItemSimpleForUser
                                                       {
                                                           ID = workItem.Id,
                                                           Title = workItem.Name,
                                                           Content = "",
                                                           Url = String.Format(templateUrl, workItem.ProjectName, HttpUtility.UrlEncode(workItem.Name)),
                                                           
                                                       }));

            return aRet;
        }

        private void SetInfoDataonUser(Int32 id, IEnumerable<WorkItem> currentList, ref UserDetailledWorkItems myData)
        {
            using (var unitOfWork = new UnitOfWork())
            {
                var dates = DateTimeHelpers.GetDateWorkDays(DateTime.Today, Range);
                var usr = unitOfWork.UserRepositery.GetById(id);
                var activeState = new[] { WorkItemState.InProgress, WorkItemState.Reopen };
                var qry = new TFSQuery(TFS.TfsWorkItemStore);

                qry.GetScoredUserWorkItemList(usr.name, dates[0], this.FollowedProjectsDictonary);
                myData.Score = qry.GetUserScore();
                myData.Fixedbugs = qry.ScoredUserWorkItem.Count(p => p.GetWorkItemType() == WorkItemType.Defect);
                myData.Hours = (int)qry.GetUserTimeCompleted();
                myData.OpenTasks = currentList == null ? 0 :
                    currentList.Count(p => p.GetWorkItemType() == WorkItemType.Task && activeState.Contains(p.GetState()));
                myData.TotalTasks = qry.ScoredUserWorkItem.Count(p => p.GetWorkItemType() == WorkItemType.Task && p.GetState() == WorkItemState.Done);
            }
        }

        public User GetUser(Int32 currentUserId, Int32 id, CultureInfo ci)
        {
            using (var unitOfWork = new UnitOfWork())
            {
                var qry = new TFSQuery(TFS.TfsWorkItemStore);
                var uRet = (unitOfWork.UserRepositery.Filter(p => p.userid == id).Select(p => new User
                                                                                {
                                                                                    ID = p.userid,
                                                                                    Name = p.name,
                                                                                    Mail = p.mail,
                                                                                    Account = p.tfsname,
                                                                                    SID = p.tfsuserid,
                                                                                    Deleted = p.deleted,
                                                                                    Followed = p.followedby.Count(c => c.userid == currentUserId) > 0,
                                                                                    IsCurrentUser = p.userid == currentUserId
                                                                                })).FirstOrDefault();

                DateTime dStart = DateTimeHelpers.ScrumPilotStartPeriod(this.TFSParams.ScrumPilotScorePeriod, ci);
                if (uRet != null)
                {
                    qry.GetScoredUserWorkItemList(uRet.Name, dStart, this.FollowedProjectsDictonary);
                    uRet.Score = qry.GetUserScore();
                    return uRet;
                }
                return null;
            }
        }

        public User GetUser(Int32 id)
        {
            using (var unitOfWork = new UnitOfWork())
            {
                var uRet = (unitOfWork.UserRepositery.Filter(p => p.userid == id).Select(p => new User
                {
                    ID = p.userid,
                    Name = p.name,
                    Mail = p.mail,
                    Account = p.tfsname,
                    SID = p.tfsuserid,
                    Deleted = p.deleted,
                })).FirstOrDefault();

                return uRet;
            }
        }

        public List<User> GetMyTeam(Int32 currentUserID)
        {
            using (var unitOfWork = new UnitOfWork())
            {
                CurrentUserID = currentUserID;
                var usrs = unitOfWork.UserRepositery.Filter(p => !p.deleted);

                if (CurrentUser.FilterUsersList && FollowedUsers != null)
                {
                    usrs = usrs.Where(p => FollowedUsers.Contains(p.userid));
                }
                usrs = usrs.OrderBy(p => p.name);
                var lstRet = (usrs.Select(usr => new User
                                                       {
                                                           ID = usr.userid,
                                                           Name = usr.name,
                                                           Mail = usr.mail,
                                                           Account = usr.tfsname,
                                                           Deleted = usr.deleted,
                                                           Followed = usr.followedby.Count(c => c.userid == currentUserID) > 0,
                                                       })).ToList();
                return lstRet;
            }
        }

        public void SetUsersListFilter(Int32 currentUserID, Byte type)
        {
            using (var unitOfWork = new UnitOfWork())
            {
                var usr = unitOfWork.UserRepositery.GetById(currentUserID);
                usr.FilterUsersList = type == 0;
                unitOfWork.UserRepositery.Update(usr);
                unitOfWork.Save();
            }
        }

        public void FollowingUser(Int32 currentUserID, Int32 userid, Byte action)
        {
            using (var unitOfWork = new UnitOfWork())
            {
                CurrentUserID = currentUserID;
                var fur = unitOfWork.FollowedUserRepositery.Find(
                            p => p.userid == currentUserID && p.followeduserid == userid);
                if (action == 0) // Follow
                {
                    if (fur == null)
                        unitOfWork.FollowedUserRepositery.Add(new followeduser
                                                       {
                                                           userid = currentUserID,
                                                           followeduserid = userid,
                                                           since = DateTime.UtcNow
                                                       });
                }
                else
                {
                    unitOfWork.FollowedUserRepositery.Delete(fur);
                }
                unitOfWork.Save();
            }
        }

        public userimage GetUserImages(Int32 id)
        {
            using (var unitOfWork = new UnitOfWork())
            {
                return (unitOfWork.UserRepositery.GetById(id).userimage);
            }
        }

        public TFSResumeUser GetTFSUserInfo(Int32 id, TFSServices tfs)
        {
            //user usr = DB.users.FirstOrDefault(p => p.userid == id);
            //if (usr == null) return null;
            //var tfsIndentity = tfs.GetTFSIndentity(usr.tfsuserid);
            //if (tfsIndentity == null) return null;
            //var tfsUsr = new TFSResumeUser
            //{
            //    Domain = tfsIndentity.Domain,
            //    Mail = tfsIndentity.MailAddress,
            //    Description = tfsIndentity.Description,
            //    Type = tfsIndentity.Type.ToString(),
            //    ProjectRoles = new List<TFSProjectsRolesUser>()
            //};
            //tfs.GetProjectsUseBy(usr.tfsuserid).ForEach(rpfu => tfsUsr.ProjectRoles.Add(new TFSProjectsRolesUser
            //{
            //    ProjectName = rpfu.Name,
            //    ID = rpfu.ID,
            //    Roles = (rpfu.Roles.Select(
            //            p => new TFSRole
            //            {
            //                Name = p.DisplayName,
            //                Description = p.Description
            //            })).ToList()
            //}));

            //if (String.IsNullOrEmpty(usr.mail) && !String.IsNullOrEmpty(tfsUsr.Mail))
            //{
            //    // COmplete mail address in DB if it's empty
            //    usr.mail = tfsUsr.Mail;
            //    DB.SaveChanges();
            //}
            //return tfsUsr;
            return null;
        }

        public int CheckUserFromTfs()
        {
            using (var unitOfWork = new UnitOfWork())
            {
                var iRet = 0;
                var lst = TFS.GetAllTfsUsers();
                if (lst.Count > 0)
                {
                    iRet += lst.Count(identity => !unitOfWork.UserRepositery.Filter(p => p.tfsuserid == identity.Sid).Any());
                    // Not in ScrumPilot
                    var usrs = unitOfWork.UserRepositery.GetMany(p => p.deleted == false);
                    iRet += usrs.Count(usr => lst.Count(p => p.Sid == usr.tfsuserid) == 0); // Not in TFS
                }
                return iRet;
            }
        }

        public void SynchronizeTFSUsers()
        {
            using (var unitOfWork = new UnitOfWork())
            {
                var lst = TFS.GetAllTfsUsers();
                var usrs = unitOfWork.UserRepositery.GetMany(p => p.deleted == false).ToArray();

                if (lst.Count > 0 && usrs.Any())
                {
                    // ReSharper disable AccessToDisposedClosure
                    var ulst = lst.Where(identity => !unitOfWork.UserRepositery.Filter(p => p.tfsuserid == identity.Sid).Any()).ToArray();
                    // ReSharper restore AccessToDisposedClosure
                    foreach (var identity in ulst)
                    {
                        // Not in SP
                        this.CheckIdentity(identity.Sid, false);
                    }

                    foreach (user usr in usrs.Where(usr => lst.Count(p => p.Sid == usr.tfsuserid) == 0))
                    {
                        // don't exist in TFS => delete it
                        usr.deleted = true;
                        unitOfWork.UserRepositery.Update(usr);
                        unitOfWork.Save();
                    }

                }
            }
        }

        /// <summary>
        /// Create a registred ScrumPilot user base on indentity
        /// </summary>
        /// <param name="sid">TFS Id (or account name)</param>
        /// <param name="bName">Use Id(false) or Account name for find TFSUser</param>
        /// <returns>a new user</returns>
        public user CreateUserBaseOnTFSIdentity(String sid, bool bName)
        {
            return
                this.CreateUserBaseOnTFSIdentity((bName
                                                      ? TFS.GetTFSIndentityByAccountName(sid)
                                                      : TFS.GetTFSIndentity(sid)));
        }

        /// <summary>
        /// Create a registred ScrumPilot user base on indentity
        /// </summary>
        /// <param name="idt">Identity object</param>
        /// <returns>a new user</returns>
        public user CreateUserBaseOnTFSIdentity(Identity idt)
        {
            user newUser = null;
            if (idt != null)
            {
                newUser = new user
                {
                    name = idt.DisplayName,
                    tfsuserid = idt.Sid,
                    tfsname = idt.Domain + "\\" + idt.AccountName,
                    deleted = idt.Deleted,
                    isadmin = TFS.IsAdmin(idt)
                };
            }
            return newUser;
        }

        /// <summary>
        /// Check if a user is registred in ScrumPilot, if not, it create it
        /// </summary>
        /// <param name="sId">TFS Id (or account name)</param>
        /// <param name="bName">Use Id(false) or Account name for find TFSUser</param>
        /// <returns>ID of the ne user</returns>
        public int CheckIdentity(string sId, bool bName)
        {
            var usr = new user();
            return this.CheckIdentity(sId, bName, ref usr);
        }

        public int CheckIdentity(string sId, bool bName, ref user usr)
        {
            int iRet = -1;
            using (var unitOfWork = new UnitOfWork())
            {
                try
                {
                    if (!String.IsNullOrWhiteSpace(sId))
                    {
                        usr = !bName
                                  ? unitOfWork.UserRepositery.Get(p => p.tfsuserid == sId)
                                  : unitOfWork.UserRepositery.Get(p => p.tfsname == sId);
                        if (usr != null) // Find it
                            iRet = usr.userid;
                        else // Auto Create
                        {
                            usr = (TFS.currentUser != null && TFS.currentUser.Sid == sId)
                                      ? this.CreateUserBaseOnTFSIdentity(TFS.currentUser)
                                      : this.CreateUserBaseOnTFSIdentity(sId, bName);
                            if (usr != null)
                            {
                                unitOfWork.UserRepositery.Add(usr);
                                unitOfWork.Save();
                                iRet = usr.userid;
                            }
                        }
                    }
                    else
                        iRet = -2;
                }
                catch (Exception)
                {
                    iRet = -3;
                }
            }
            return iRet;
        }
    }
}
